#ifndef __M_HELPER_H__
#define __M_HELPER_H__
#include <iostream>
#include <string>
#include <cstdio>
#include <vector>
#include <random>
#include <filesystem>
#include <cstring>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <atomic>
#include <sys/stat.h>
#include <sqlite3.h>
#include "log.hpp"

namespace pprmq
{
    class Sqlitehelper
    {
        typedef int (*SqliteCallback)(void *, int, char **, char **);

    public:
        Sqlitehelper(const std::string dbfile) : _dbfile(dbfile), _handler(nullptr) {}
        bool open(int safe_level = SQLITE_OPEN_FULLMUTEX)
        {
            // int sqlite3_open_v2(const char*filename,sqlite3 **ppDb,...) 成功返回SQLITE_OK
            int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | safe_level, nullptr);
            if (ret != SQLITE_OK)
            {
                ELOG("创建/打开sqlite数据库失败:%s", sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        bool exec(const std::string &sql, SqliteCallback cb, void *arg)
        {
            int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
            if (ret != SQLITE_OK)
            {
                ELOG("%s\n执行失败:%s", sql.c_str(), sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        bool close()
        {
            if (_handler)
                sqlite3_close_v2(_handler);
            return true;
        }

    private:
        std::string _dbfile;
        sqlite3 *_handler;
    };

    class StrHelper
    {
    public:
        static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &result)
        {
            // news.music.#.pop
            // 分割的思想：
            //  1. 从0位置开始查找指定字符的位置， 找到之后进行分割
            //  2. 从上次查找的位置继续向后查找指定字符
            size_t pos, idx = 0;
            while (idx < str.size())
            {
                pos = str.find(sep, idx);
                if (pos == std::string::npos)
                {
                    // 找完了
                    result.push_back(str.substr(idx));
                    return result.size();
                }
                // 如果pos位置和idx相等，就证明是两个分隔符连在一起的，直接将idx增加后continue
                if (idx == pos)
                {
                    idx = pos + sep.size();
                    continue;
                }

                // 找到中间位置
                result.push_back(str.substr(idx, pos - idx));
                idx = pos + sep.size();
            }
            return result.size();
        }
    };

    class UUIDHelper
    {
    public:
        static std::string uuid()
        {
            // 生成一个硬件随机数，效率较低
            std::random_device rd;
            // size_t num = rd();
            // 因此解决方案，就是通过一个机器随机数作为生成伪随机数的种子
            // 通过梅森旋转算法，生成一个伪随机数
            std::mt19937_64 generator(rd());                         // 种种子
            std::uniform_int_distribution<int> distribution(0, 255); // 生成的随机数定范围0~255

            std::stringstream ss;
            for (int i = 0; i < 8; i++)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
                if (i == 3 || i == 5 || i == 7)
                    ss << "-";
            }

            static std::atomic<int> sep(1); // 定义一个原子类，初始化为1，作为序号
            size_t id = sep.fetch_add(1);   // 每次都增加1
            for (int i = 7; i >= 0; i--)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << ((id >> (i * 8)) & 0xff);
                if (i == 6)
                    ss << "-";
            }
            return ss.str();
        }
    };

    class FileHelper
    {
    public:
        FileHelper(const std::string &filename) : _filename(filename)
        {
        }

        // 检查文件是否存在
        size_t exists()
        {
            struct stat st;
            return !stat(_filename.c_str(), &st);
            // return (stat(_filename.c_str(), &st) == 0);
        }
        // 文件大小是多少
        size_t size()
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if (ret < 0)
                return 0;
            return st.st_size;
        }
        // 将读到的数据存放到body里面
        bool read(char *body, size_t pos, size_t len)
        {
            // 打开文件
            std::ifstream ifs;
            ifs.open(_filename, std::ios::binary | std::ios::in);
            if (!ifs.is_open())
            {
                // 打开失败（如文件不存在、权限不足）
                ELOG("%s 文件打开失败:%s", _filename.c_str(), strerror(errno));
                return false;
            }
            // 跳转指定位置
            ifs.seekg(pos, ifs.beg);
            // 读取文件数据
            ifs.read(body, len);
            if (!ifs.good())
            {
                ELOG("%s 文件读取失败:%s", _filename.c_str(), strerror(errno));
                ifs.close();
                return false;
            }
            // 关闭文件
            ifs.close();
            return true;
        }
        bool read(std::string &body)
        {
            // 获取文件大小，根据文件大小调整body的空间
            size_t sz = this->size();
            body.resize(sz);
            return read(&body[0], 0, sz);
        }
        // body为写入该文件的数据
        bool write(const char *body, size_t pos, size_t len)
        {
            // 打开文件
            std::fstream fs;
            fs.open(_filename, std::ios::binary | std::ios::in | std::ios::out); // 可读可写
            if (!fs.is_open())
            {
                // 打开失败（如文件不存在、权限不足）
                ELOG("%s 文件打开失败:%s", _filename.c_str(), strerror(errno));
                return false;
            }
            // 跳转指定位置
            fs.seekp(pos, fs.beg);
            // 写入文件数据
            fs.write(body, len);
            if (!fs.good())
            {
                ELOG("%s 文件写入失败:%s", _filename.c_str(), strerror(errno));
                fs.close();
                return false;
            }
            // 关闭文件
            fs.close();
            return true;
        }
        bool write(const std::string &body)
        {
            return write(body.c_str(), 0, body.size());
        }
        // 文件重命名
        bool rename(const std::string &newname)
        {
            return !(::rename(_filename.c_str(), newname.c_str()));
        }

        // 获取当前文件父级目录
        static std::string parentDirectory(const std::string &filename)
        {
            // /aaa/bb/ccc/ddd/test.txt
            size_t pos = filename.find_last_of("/");
            if (pos == std::string::npos)
            {
                // test.txt
                return "./";
            }
            std::string path = filename.substr(0, pos);
            return path;
        }

        // 创建文件
        static bool createFile(const std::string &filename)
        {
            std::fstream fs(filename, std::ios::binary | std::ios::out);
            if (!fs.is_open())
            {
                // 打开失败（如文件不存在、权限不足）
                ELOG("%s 文件打开失败:%s", filename.c_str(), strerror(errno));
                return false;
            }
            fs.close();
            return true;
        }
        // 删除文件
        static bool removeFile(const std::string &filename)
        {
            return !(::remove(filename.c_str()));
        }
        // 创建目录
        static bool createDirectory(const std::string &path)
        {
            size_t pos = 0, idx = 0;
            while (idx < path.size())
            {
                pos = path.find("/",idx);
                if (pos == std::string::npos)
                {
                    return !mkdir(path.c_str(), 0775);
                }
                std::string gpath = path.substr(0,pos);
                int ret = mkdir(gpath.c_str(),0775);
                if (ret != 0 && errno != EEXIST) // 判断 操作失败的原因是否不是 文件/目录已存在
                {
                    ELOG("创建目录 %s 失败: %s", gpath.c_str(), strerror(errno));
                    return false;
                }
                idx = pos + 1;
            }
            // return std::filesystem::create_directories(path);
            return true;
        }
        // 删除目录
        static bool removeDirectory(const std::string &path)
        {
            std::string cmd = "rm -fr " + path;
            return system(cmd.c_str());
        }

        ~FileHelper()
        {
        }

    private:
        std::string _filename;
    };

}
#endif